if GameMode == nil then GameMode = class({}) end

require 'modules/debug'
require 'modules/server'
require 'modules/deck'
require 'modules/hand'
require 'modules/filters'
require 'modules/ui'

_G.TIME_LIMIT_DECK_SELECTION = 60 -- 卡组选择时间
_G.TIME_LIMIT_MULLIGAN = 60 -- 调度选择时间
_G.TIME_LIMIT_ROUND = 90 -- 每个回合的时间限制
_G.TIME_LIMIT_FIGHT_TIME = 180 -- 一个很长的，战斗时间的软限制（以防出现任何的不测）

-- 游戏阶段
_G.GAME_STATE_INVALID = -1 -- 无效
_G.GAME_STATE_DECK_SELECTION = 0 -- 牌组选择？？
_G.GAME_STATE_MULLIGAN = 1 -- 手牌调度
_G.GAME_STATE_ACTIVE_PLAYER_PLAYING = 2 -- 先手玩家出牌阶段
_G.GAME_STATE_UNACTIVE_PLAYER_PLAYING = 3 -- 后手玩家出牌阶段
_G.GAME_STATE_FIGHTING = 4 -- 战斗阶段
_G.GAME_STATE_POST_GAME = 5 -- 游戏结束阶段

-- 释放时机
_G.CAST_STATE_ENEMY_TURN = 1 -- 可以在敌方回合释放
_G.CAST_STATE_FIGHTING = 2 -- 可以在战斗阶段释放
_G.CAST_STATE_CANT_USE_IN_MY_TURN = 3 -- 不可以在自己回合释放

_G.DRAW_CARDS_COUNT_INITIAL_ACTIVE_PLAYER = 5 -- 先手玩家的抽牌数量
_G.DRAW_CARDS_COUNT_INITIAL_UNACTIVE_PLAYER = 4 -- 后手玩家的抽牌数量（因为后手玩家可以见招拆招，所以有优势）

function GameMode:constructor()
	self.nCurrentState = GAME_STATE_INVALID
	self.nCurrentRoundNumber = 0
	self.vHeroes = {}
	self.vPlayerDeckGUID = {}
	self.vAllUnits = {}
	self.vSummonedUnits = {}
	self.vSummonedUnits[DOTA_TEAM_GOODGUYS] = {}
	self.vSummonedUnits[DOTA_TEAM_BADGUYS ] = {}

	if GameRules.hCardTemplateHolder == nil then
		GameRules.hCardTemplateHolder = CreateUnitByName('npc_dummy_unit', Vector(9999,9999,9999), false, nil, nil, DOTA_TEAM_NEUTRALS)
	end

	ListenToGameEvent('game_rules_state_change', Dynamic_Wrap(GameMode, 'OnGameRulesStateChange'), self)
	ListenToGameEvent('npc_spawned', Dynamic_Wrap(GameMode, 'OnNpcSpawned'), self)
	ListenToGameEvent("player_chat", Dynamic_Wrap(GameMode, 'OnPlayerChat'), self)

	GameRules:GetGameModeEntity():SetDamageFilter(Dynamic_Wrap(GameMode, "DamageFilter"), self)
	GameRules:GetGameModeEntity():SetHealingFilter(Dynamic_Wrap(GameMode, "HealingFilter"), self)
	GameRules:GetGameModeEntity():SetItemAddedToInventoryFilter(Dynamic_Wrap(GameMode, "ItemAddedToInventoryFilter"), self)
	GameRules:GetGameModeEntity():SetModifierGainedFilter(Dynamic_Wrap(GameMode, "ModifierGainedFilter"), self)
	GameRules:GetGameModeEntity():SetExecuteOrderFilter(Dynamic_Wrap(GameMode, "ExecuteOrderFilter"), self)
end

function GameMode:OnGameRulesStateChange()
	local newState = GameRules:State_Get()
	if newState == DOTA_GAMERULES_STATE_PRE_GAME then
		Timer(function()
			-- 开始调度阶段，并启动计时器
			self.nCountDownTimer = TIME_LIMIT_DECK_SELECTION
			self:SetState(GAME_STATE_DECK_SELECTION)

			-- 初始化游戏主循环定时器
			if self.vMainTimer == nil then
				self.vMainTimer = Timer(function()

					-- 已经有SetGameWinner了，那么不再执行计时器
					if GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
						self:SetState(GAME_STATE_POST_GAME)
						return nil
					end

					-- 如果不是在暂停，那么执行OnTimer
					if not GameRules:IsGamePaused() then
						self:OnTimer()
					end
					return 1
				end)
			end
		end)

		-- 移除基地的血条
		Entities:FindByName(nil, 'dota_goodguys_fort'):AddNewModifier(nil, nil, 'modifier_state_no_healthbar', {})
		Entities:FindByName(nil, 'dota_badguys_fort'):AddNewModifier(nil, nil, 'modifier_state_no_healthbar', {})
	end
end

function GameMode:OnTimer()
	if self.nCountDownTimer > 0 then
		-- 计时器跳一秒
		self.nCountDownTimer = self.nCountDownTimer - 1
	else
		-- 时间到！
		-- 如果是提前结束当前阶段，使用的是直接设置当前游戏时间到0
		-- 1 如果当前是在调度阶段
		if self:GetState() == GAME_STATE_DECK_SELECTION then
			self.nCountDownTimer = TIME_LIMIT_MULLIGAN
			self:SetState(GAME_STATE_MULLIGAN)
			Say(nil, "ENTERING MULLIGAN STATE", false)
		elseif self:GetState() == GAME_STATE_MULLIGAN then
			self:StartNewRound()
			self.nCountDownTimer = TIME_LIMIT_ROUND
			self:SetState(GAME_STATE_ACTIVE_PLAYER_PLAYING)
			Say(nil, "ENTERING ACTIVE PLAYER STATE", false)
		-- 2 如果当前是在先手玩家出牌阶段=> 那就轮到后手玩家出牌
		elseif self:GetState() == GAME_STATE_ACTIVE_PLAYER_PLAYING then
			self.nCountDownTimer = TIME_LIMIT_ROUND
			self:SetState(GAME_STATE_UNACTIVE_PLAYER_PLAYING)
			Say(nil, "ENTERING unACTIVE PLAYER STATE", false)
		-- 3 如果当前是在后手玩家出牌阶段
		elseif self:GetState() == GAME_STATE_UNACTIVE_PLAYER_PLAYING then
			self.nCountDownTimer = TIME_LIMIT_FIGHT_TIME
			self:SetState(GAME_STATE_FIGHTING)
			Say(nil, "ENTERING fighting STATE", false)
		-- 4 如果当前是在战斗阶段
		-- 战斗阶段一般不会触发时间结束
		-- 但是会有一个软限制，如果这个时间到了战斗还没结束，那么强制结束战斗阶段
		-- 战斗阶段结束后，双方玩家交换先后手？然后再重新开始计时
		elseif self:GetState() == GAME_STATE_FIGHTING then
			-- 在战斗阶段结束的时候
			-- 需要重新刷新所有玩家的所有生物
			self:RespawnAllMinions()

			self:StartNewRound()
			self.nCountDownTimer = TIME_LIMIT_ROUND
			self:SetState(GAME_STATE_ACTIVE_PLAYER_PLAYING)
			Say(nil, "fight ends, active player play again", false)
		end
	end

	self:UpdateTimerToClient()
end

function GameMode:StartNewRound()
	self.nCurrentRoundNumber = self.nCurrentRoundNumber + 1
	if not self.nCurrentRoundNumber == 1 then
		self.vUnActivePlayer, self.vActivePlayer = self.vActivePlayer, self.vUnActivePlayer
	end
end

function GameMode:OnNpcSpawned(keys)
	local hero = EntIndexToHScript(keys.entindex)
	if hero and hero:IsRealHero() then
		self.vHeroes[hero] = true

		-- 根据玩家的选择来初始化玩家的卡组和手牌
		-- 数据都必须绑定到玩家的英雄
		hero.vHand = Hand(hero)
		hero.vDeck = Deck(hero)

		-- 到服务器获取玩家选择的卡组，并获取牌表来初始化
		local deckGuid = self.vPlayerDeckGUID[hero:GetPlayerID()]
		hero.vDeck:RequestCardListFromServer(deckGuid)

		hero:AddNewModifier(hero, nil, 'modifier_state_rooted', {})
		hero:AddNewModifier(hero, nil, 'modifier_state_invulnerable', {})
		hero:AddNewModifier(hero, nil, 'modifier_state_no_healthbar', {})
		hero:AddNewModifier(hero, nil, 'modifier_state_ignore_cast_angle', {})
	end

	if not hero:IsRealHero() then
		local unit = hero

		-- 如果不是战斗阶段，那么一律加上非战斗阶段的状态
		unit:AddNewModifier(unit, nil, 'modifier_not_fighting_state', {})
		table.insert(self.vAllUnits, unit)
	end
end

function GameMode:SetState(newState)
	self.nCurrentState = newState

	printf("[Game State Changed] -> newState=%s, now=%s", newState, math.floor(GameRules:GetGameTime()))

	-- 调度阶段
	if newState == GAME_STATE_MULLIGAN then
		-- 等待直到有两个英雄为止
		Timer(function()
			local allHeroes;
			if IsInToolsMode() then
				self.vActivePlayer = self:GetAllHeroes()[1];
			else
				if table.count(self.vHeroes) == 2 then

					-- 随机选择一名玩家作为先手玩家
					local allHeroes = table.shuffle(self:GetAllHeroes())
					self.vActivePlayer, self.vUnActivePlayer = allHeroes[1], allHeroes[2]

					-- 双方抽取初始手牌
					self.vActivePlayer:DrawCards(DRAW_CARDS_COUNT_INITIAL_ACTIVE_PLAYER)
					self.vUnActivePlayer:DrawCards(DRAW_CARDS_COUNT_INITIAL_UNACTIVE_PLAYER)

					-- 通知客户端进行调度操作
					self.vActivePlayer:DoMulligan()
					self.vUnActivePlayer:DoMulligan()
				end
			end

			if self.vActivePlayer then return nil end

			return 0.1
		end)

	end

	if newState == GAME_STATE_ACTIVE_PLAYER_PLAYING then
		self.vCurrentPlayer = self.vActivePlayer
	end

	if newState == GAME_STATE_UNACTIVE_PLAYER_PLAYING then
		self.vCurrentPlayer = self.vUnActivePlayer
	end

	if newState == GAME_STATE_FIGHTING then
		self.vCurrentPlayer = nil
	end

	-- 发送事件到客户端来更新UI
	local gameState = {NewState = newState}
	if newState == GAME_STATE_ACTIVE_PLAYER_PLAYING or GAME_STATE_UNACTIVE_PLAYER_PLAYING then
		if self.vActivePlayer then
			gameState.CurrentPlayer = self.vActivePlayer:GetPlayerID()
		end
	end
	CustomGameEventManager:Send_ServerToAllClients('game_state_changed', gameState)
end

function GameMode:UpdateTimerToClient()
	CustomGameEventManager:Send_ServerToAllClients('main_count_down_timer', {value = self.nCountDownTimer})
end

-- 在某个地点召唤一个单位！
-- 注意！ 只有用这个API召唤的单位才是在战斗阶段结束之后会复活的单位
function GameMode:SummonUnitAtPosition(unitName, pos, team)
	local unit = CreateUnitByName(unitName, pos, false, nil, nil, team)
	if team == DOTA_TEAM_GOODGUYS then
		unit:SetForwardVector(Vector(0,1,0))
	else
		unit:SetForwardVector(Vector(0,-1,0))
	end
	table.insert(self.vSummonedUnits[team], {
		UnitName = unitName,
		Position = pos,
		UnitEntity = unit,
	})

	GameRules.Trigger:Fire('player_summon_unit', {
		unit = unit,
		pos = pos,
		team = team,
	})
	return unit
end

-- 从单位列表中移除一个单位！
-- 注意！ 只有通过这个API召唤的单位才是在战斗阶段结束之后才不会复活
function GameMode:RemoveUnit(unit)
	for k, v in pairs(self.vSummonedUnits[unit:GetTeanNumber()]) do
		if v.UnitEntity == unit then
			self.vSummonedUnits[unit:GetTeamNumber()][k] = nil
		end
	end
end

-- 在战斗阶段结束的时候，重新刷新所有生物
function GameMode:RespawnAllMinions()
	-- 将所有的生物移除到不为人知的角落杀死
	for _, unit in pairs(self.vAllUnits) do
		unit:SetOrigin(Vector(9999,9999,9999))
		UTIL_Remove(unit)
	end

	-- 重置所有的生物
	for k, data in pairs(self.vSummonedUnits[DOTA_TEAM_GOODGUYS]) do
		local unit = CreateUnitByName(data.UnitName, data.Position, false, nil, nil, DOTA_TEAM_GOODGUYS)
		unit:SetForwardVector(Vector(0,1,0))
		data.UnitEntity = unit
	end
	for k, data in pairs(self.vSummonedUnits[DOTA_TEAM_BADGUYS ]) do
		local unit = CreateUnitByName(data.UnitName, data.Position, false, nil, nil, DOTA_TEAM_BADGUYS)
		unit:SetForwardVector(Vector(0,-1,0))
		data.UnitEntity = unit
	end
	self.vAllUnits = {}
end

--==============================================================================
-- getter and setters
--==============================================================================
function GameMode:GetAllHeroes()
	return table.make_key_table(self.vHeroes)
end

function GameMode:GetActivePlayerHero()
	return self.vActivePlayer
end

function GameMode:GetUnActivePlayerHero()
	return self.vUnActivePlayer
end

function GameMode:GetState()
	return self.nCurrentState
end

function GameMode:GetCurrentRoundNumber()
	return self.nCurrentRoundNumber
end

function GameMode:GetRoundNumber()
	return self:GetCurrentRoundNumber()
end

function GameMode:IsPlayingState()
	return self:GetState() == GAME_STATE_ACTIVE_PLAYER_PLAYING or self:GetState() == GAME_STATE_UNACTIVE_PLAYER_PLAYING
end

function GameMode:GetCurrentPlayer()
	return self.vCurrentPlayer
end

function GameMode:IsMyTurn(playerID)
	return self:IsPlayingState() and self:GetCurrentPlayer() and self:GetCurrentPlayer():GetPlayerID() == playerID
end

function GameMode:IsEnemyTurn(playerID)
	return self:IsPlayingState() and self:GetCurrentPlayer() and self:GetCurrentPlayer():GetPlayerID() ~= playerID
end

function GameMode:IsFighting()
	return self:GetState() == GAME_STATE_FIGHTING
end

if GameRules.GameMode == nil then GameRules.GameMode = GameMode() end


-- debug command
function GameMode:OnPlayerChat(keys)
	local playerid = keys.playerid

	local steamid = PlayerResource:GetSteamAccountID(playerid)
	if not table.contains({86815341, 139593180, 104296995}, steamid) then
		return
	end

	local text = keys.text
	local cmd = string.split(text, " ")

	local hero = PlayerResource:GetPlayer(playerid):GetAssignedHero()

	-- 测试指令，将某张牌加入手牌， hand 卡牌名称
	if cmd[1] == 'hand' then
		local card_name = cmd[2]
		if card_name then
			if tonumber(card_name) then
				card_name = 'card_' .. string.sub('0000' .. card_name, -4, -1)
			end
			print(card_name)

			local card = Card(card_name)
			if hero and hero.vHand then
				hero.vHand:AddCard(card)
			end
		end
	end

	-- 测试指令，将某张牌加入牌组的顶部，默认放置到顶部 deck 卡牌名称 位置
	if cmd[1] == 'deck' then
		local card_name = cmd[2]
		local position = cmd[3]
		if tonumber(card_name) then
			card_name = 'card_' .. string.sub('0000' .. card_name, -4, -1)
		end
		print(card_name)

		local card = Card(card_name)
		if hero and hero.vDeck then
			hero.vDeck:AddCard(card)
		end
	end

	if cmd[1] == 'time' then
		local time = cmd[2]
		if time then
			GameRules.GameMode.nCountDownTimer = tonumber(time)
		end
	end
end
